package controllers

import akka.actor.Actor
import play.api.Logger
import akka.util.duration._
import play.api.libs.concurrent.Akka
import play.api.Play.current
import akka.actor.Props
import java.util.Date
import transcoding.Other
import play.api.cache.Cache
import java.util.concurrent.ConcurrentHashMap
import scala.collection.mutable.ConcurrentMap
import scala.collection.JavaConversions._
import java.util.concurrent.atomic.AtomicReference
import com.xuggle.xuggler.IMetaData

object LibraryManager {
  class LibraryManagerActor extends Actor {

    def receive = {
      case 'Update =>
        Logger.debug("Updating Library...")
        mediaLibrary.lazySet(updateLibrary(getLibraryDir))
        Logger.debug("...DONE updating library")
      case message => Logger.debug("Could not understand: " + message.toString)
    }
  }
  //TODO use cache (with different namespace) to store this
  private val mediaLibrary = new AtomicReference[ConcurrentHashMap[transcoding.MediaType, ConcurrentHashMap[Int, transcoding.MediaFileManager]]]

  def getLibraryDir = {
    "/home/giacomo/Videos/"
  }

  def getAltLibraryDir = {
    "/home/giacomo/Videos/.blade/altMedia/"
  }

  def getMediaLibrary = {
    val l = this.mediaLibrary.get
    if (l == null) None
    else Some(l)
  }

  val actor = Akka.system.actorOf(Props[LibraryManagerActor], name = "fileManagerActor")
  Akka.system.scheduler.schedule(0 seconds, 35 minutes, actor, 'Update)

  def updateLibrary(dirPath: String) = {
    val mediaDir = new java.io.File(dirPath)
    val altMediaDir = new java.io.File(getAltLibraryDir)

    if (!mediaDir.isDirectory())
      throw new IllegalArgumentException("The media path '%s' is not pointing to a directory!".format(mediaDir.getAbsolutePath))
    if (!altMediaDir.isDirectory())
      throw new IllegalArgumentException("The media path '%s' is not pointing to a directory!".format(altMediaDir.getAbsolutePath))

    val library = new ConcurrentHashMap[transcoding.MediaType, ConcurrentHashMap[Int, transcoding.MediaFileManager]]
    addAllMediaFiles(mediaDir)(library)
    addAlternativeFormats(altMediaDir)(library)

    library
  }

  def getMediaFile(fileID: Int) = {

    def getMF(library: ConcurrentHashMap[transcoding.MediaType, ConcurrentHashMap[Int, transcoding.MediaFileManager]]): Option[transcoding.MediaFileManager] = {
      for (entry <- library.entrySet) {
        entry.getKey match {
          case transcoding.Other => None

          case _ =>
            val mf = entry.getValue.get(fileID)
            if (mf != null) return Some(mf)
        }
      }
      return None
    }

    getMediaLibrary.map(library => getMF(library)).getOrElse(None)
  }
  
  def addAlternativeFormat(origMF:transcoding.MediaFile, altMediaFile: java.io.File) = {
    getMediaFile(origMF.filePath.hashCode) match{
      case Some(mfm) => mfm.addOtherVersion(transcoding.MediaFile(altMediaFile)) 
    		  getMediaLibrary.get.get(origMF.mediaType).put(origMF.filePath.hashCode, mfm)
      case None => throw new RuntimeException("Could not find %s in MediaLibrary".format(origMF.filePath))
    }
    
    ()
  }
  
  private def addAlternativeFormats(altMediaDir: java.io.File)(library: ConcurrentHashMap[transcoding.MediaType, ConcurrentHashMap[Int, transcoding.MediaFileManager]]): Unit = {
    altMediaDir.listFiles()
      .foreach(f =>
        if (f.isDirectory()) addAlternativeFormats(f)(library)
        else {
          try {
            val mf: transcoding.MediaFile = transcoding.MediaFile(f)
            mf.mediaType match {
              case transcoding.Image =>
              case transcoding.Other =>
              case media =>
                val originalFilePath = mf.metaData.map(md => md.getValue("grouping").trim).getOrElse("")

                if (!originalFilePath.isEmpty()) {
                  Logger.debug("*******Found alt version for " + originalFilePath+ "mediaType = "+media)
                  val mediaLibrary = library.get(media)
                  if (null != mediaLibrary && mediaLibrary.containsKey(originalFilePath.hashCode)) {
                    Logger.error("*******ADDING alt version for " + originalFilePath)
                    mediaLibrary.get(originalFilePath.hashCode).addOtherVersion(mf)

                  } else f.delete
                } else f.delete
            }
          } catch { case _ => }
        })
  }

  private def addAllMediaFiles(mediaDir: java.io.File)(library: ConcurrentHashMap[transcoding.MediaType, ConcurrentHashMap[Int, transcoding.MediaFileManager]]): Array[transcoding.MediaFileManager] = {
    mediaDir.listFiles(new java.io.FileFilter { override def accept(fileName: java.io.File) = { !fileName.isHidden } })
      .flatMap(f =>
        if (f.isDirectory()) addAllMediaFiles(f)(library)
        else {
          try {
            val mf: transcoding.MediaFile = transcoding.MediaFile(f)
            mf.mediaType match {
              case media =>
                library.getOrElseUpdate(media, new ConcurrentHashMap[Int, transcoding.MediaFileManager]).put(mf.filePath.hashCode, new transcoding.MediaFileManager(mf))
                Seq()
            }
          } catch { case _ => Seq() }
        })
  }
}